/*
 * LoggerManager.java
 *
 *  created: 3.8.2011
 *  charset: UTF-8
 *  license: MIT (X11) (See LICENSE file for full license)
 */

package cz.mp.k3bg.log;

import cz.mp.k3bg.Application;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Třída {@code LoggerManager} je správce logování.
 * Pro jednotný způsob práce s logováním.
 *
 * @author Martin Pokorný
 * @version 0.1
 */
public class LoggerManager {
    
    /** */
    public static final Level DEFAULT_NORMAL_LEVEL = Level.FINE;
    /** */
    public static final Level DEFAULT_DEBUG_LEVEL = Level.FINE;
    /** Maximální velikost logovacího souboru. */
    private static final int LOG_FILE_MAX_LEN = 10*1024*1024;

    /** 
     * Handler pro ladění, který posílá detailní zprávy na 
     * <code>stderr</code>.
     */
    private static ConsoleHandler debugHandler = null;

    /** Handler posílající zprávy do souboru. */
    private static FileHandler logFileHandler = null;

    /** Jméno souboru, do kterého se má logovat. */
    private static String logFileName = Application.DEFAULT_LOG_FILE_NAME;
    
    /** 
     * Jméno kořenového, hlavního Loggeru.
     * viz {@linkplain #getClassLogger(java.lang.Class)}
     */
    static final String MAIN_LOGGER_NAME = 
            Application.NAME_SHORT.replaceAll("\\s", "-");
    
    /** 
     * Hlavní, rodičovský Logger pro tento projekt.
     */
    @SuppressWarnings("NonConstantLogger")
    private static Logger MAIN_LOGGER = null;


    // -----

    /** Žádný konstruktor. */
    private LoggerManager() {
    }

    /**
     * Inicializace hlavního loggeru a inicializace handlerů.
     * 
     * @see #initMainLogger()
     * @see #reInitDebugHandler() 
     * @see #reInitLogFileHandler() 
     */
    private static void init() {
        initMainLogger();
        
        if (debugHandler == null) {
            reInitDebugHandler();
        }        
        if (logFileHandler == null) {
            reInitLogFileHandler();   
        }
    }
    
    /**
     * Inicializuje hlavní Logger ({@linkplain #MAIN_LOGGER}).
     */
    private static void initMainLogger() {
        if (MAIN_LOGGER == null) {
            MAIN_LOGGER = Logger.getLogger(MAIN_LOGGER_NAME);
            
            MAIN_LOGGER.setLevel(Level.ALL);

            MAIN_LOGGER.setUseParentHandlers(false);
        }
    }
    

    /**
     * @throws IllegalStateException  pokud se nepodaří inicializovat hlavní log
     */
    private static void reInitDebugHandler() {        
        try {
            debugHandler = new ConsoleHandler();
            debugHandler.setEncoding(Application.CONSOLE_DEFAULT_CHARSET);
            debugHandler.setLevel(Level.FINEST);
            debugHandler.setFormatter(new DetailFormatter());
        } catch (SecurityException ex) {
            throw new IllegalStateException(ex.getMessage());
        } catch (UnsupportedEncodingException ex) {
            throw new IllegalStateException(ex.getMessage());
        }
    }
    
    /**
     *
     * @throws IllegalStateException  pokud není inicializovaný hlavní logger
     */
    private static void reInitLogFileHandler() {
        removeLogFileHandler();
                    
        try {
            logFileHandler = new FileHandler(
                    LoggerManager.logFileName, LOG_FILE_MAX_LEN, 1, false);
            logFileHandler.setLevel(Level.FINEST);
            logFileHandler.setEncoding(Application.DEFAULT_CHARSET);
            logFileHandler.setFormatter(new DetailFormatter());            
        } catch (IOException ex) {
            throw new IllegalStateException(ex.getMessage());
        } catch (SecurityException ex) {
            throw new IllegalStateException(ex.getMessage());
        }
        
        addLogFileHandler();
    }

    /**
     * 
     */
    private static void removeLogFileHandler() {
        if (logFileHandler != null) {
            if (MAIN_LOGGER == null) {
                throw new IllegalStateException("MAIN_LOGGER=null");
            }
            MAIN_LOGGER.removeHandler(logFileHandler);
            logFileHandler.close();
            logFileHandler = null;
        }        
    }
    
    /**
     * Přidá {@linkplain #logFileHandler} do hlavního Loggeru.
     */
    private static void addLogFileHandler() {
        if (MAIN_LOGGER == null) {
            throw new IllegalStateException("MAIN_LOGGER=null");
        }
        MAIN_LOGGER.addHandler(logFileHandler);
    }
      
    
    /**
     * Nastaví jméno souboru, kam se má logovat a inicilizuje obsluhu 
     * zápisu logů do souboru.
     *
     * @param logFileName
     * @see #reInitLogFileHandler() 
     */
    public static void setLogFileName(String logFileName) {
        if (logFileName == null || logFileName.isEmpty()) {
            throw new IllegalArgumentException("logFileName is blank");
        }
        
        LoggerManager.logFileName = logFileName;

        reInitLogFileHandler();
    }

    /**
     * Získá jméno souboru, kam se má logovat.
     * 
     * @return 
     */
    public static String getLogFileName() {
        return LoggerManager.logFileName;
    }
    
    /**
     * Získá Logger, který je ve stejné cestě jako je cesta jeho balíku,
     * pouze navíc s kořenem: {@linkplain #MAIN_LOGGER_NAME}.
     * <p>
     * např: balík je {@literal cz.pokus.Trida}, 
     * log je v: {@literal MAIN_LOGGER_NAME.cz.pokus.Trida}.
     * 
     * @param clazz
     * @return 
     */
    private static Logger getClassLogger(Class clazz) {
        Logger log = Logger.getLogger(
                MAIN_LOGGER_NAME + "." +
                clazz.getName());           // cz.pokus.Trida
        
        log.setUseParentHandlers(true);
        
        return log;
    }
    
    /**
     * Získá logger k zadané třídě.
     * 
     * @param clazz
     * @param debugClazz  zda se má provádět ladící protokolování zadané třídy
     * @return 
     */
    public static Logger getLogger(Class clazz, boolean debugClazz) {
        return getLogger(clazz, debugClazz, DEFAULT_DEBUG_LEVEL);
    }
    
    /** 
     * Získá logger k zadané třídě.
     * 
     * @param clazz
     * @param debugClazz  zda se má provádět ladící protokolování zadané třídy
     * @param debugLogLevel  od jaké úrovně se má provádět ladící 
     *      protokolování zadané třídy.
     * @return 
     */
    public static Logger getLogger(Class clazz, boolean debugClazz, 
            Level debugLogLevel) {
        if (clazz == null || clazz.getName().isEmpty()) {
            throw new IllegalArgumentException("clazz is blank");
        }
        if (debugLogLevel == null) {
            throw new IllegalArgumentException("debugLogLevel=null");
        }
        
        Level debugLogLevelCopy = debugLogLevel;
        // Application.isDebug()  je jen pro podrobný log;        
        if (Application.isDebug()) {
            debugLogLevelCopy = Level.FINEST;
        }        
                
        // inicializace hlavního loggeru a inicializace handlerů
        init();

        Logger clazzLog = getClassLogger(clazz);        
        clazzLog.setLevel(debugLogLevelCopy);
        
        // debugClazz  je pro ladící logování na stderr
        if (debugClazz) {
            if (debugHandler != null) {
                clazzLog.removeHandler(debugHandler);
                clazzLog.addHandler(debugHandler);
            }
        }
        
        if (!Application.isDebug() && !debugClazz) {
            clazzLog.setLevel(DEFAULT_NORMAL_LEVEL);            
        }
        
        return clazzLog;
    }

}   // LoggerManager
